/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* define to prevent recursive inclusion -------------------------------------*/
#ifndef __K_CRITICAL_H__
#define __K_CRITICAL_H__

/* includes (standard library, system) ---------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_module.h>
#include <a_config.h>
/* includes (local) ----------------------------------------------------------*/
#include "k_errno.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* defines -------------------------------------------------------------------*/

#define CRITICAL_JOB_Q_SLOTS (CONFIG_NR_CRITICAL_JOB_Q_SLOTS)
#define CRITICAL_JOB_Q_MASK  (CRITICAL_JOB_Q_SLOTS - 1)

#define ERRNO_CRITICAL_QUEUE_FULL        ERRNO_JOIN(MID_CRITICAL, 1)
#define ERRNO_CRITICAL_ILLEGAL_OPERATION ERRNO_JOIN(MID_CRITICAL, 2)

#ifdef CONFIG_CRITICAL_STACK_CHECK
#define CRITICAL_MAGIC 0x466348614974516aull
#endif

/* typedefs ------------------------------------------------------------------*/

struct critical_job {
    int (*pfn)(uintptr_t, uintptr_t);
    uintptr_t arg1;
    uintptr_t arg2;
    uintptr_t pad;
};

#if (CONFIG_CRITICAL_STACK_SIZE & 7) != 0
#error "critical stack must be at least 8-byte aligned"
#endif

struct critical_q {
#ifdef CONFIG_CRITICAL_STACK_CHECK
    uint64_t magic;
#endif
    uint64_t stack[CONFIG_CRITICAL_STACK_SIZE >> 3];
    volatile bool in_critical;
    volatile unsigned int head_idx;
    volatile unsigned int tail_idx;
    volatile unsigned int pad;
    struct critical_job jobs[CRITICAL_JOB_Q_SLOTS];
};

/* macro ---------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/

extern struct critical_q critical_q;

/* inlines -------------------------------------------------------------------*/

static __always_inline bool in_critical(void)
{
    return critical_q.in_critical;
}

/* externs -------------------------------------------------------------------*/

extern int do_critical_might_sleep(int (*job)(uintptr_t, uintptr_t), uintptr_t arg1,
                                   uintptr_t arg2);
extern int do_critical(int (*job)(uintptr_t, uintptr_t), uintptr_t arg1, uintptr_t arg2);
extern int do_critical_non_irq(int (*job)(uintptr_t, uintptr_t), uintptr_t arg1, uintptr_t arg2);
extern bool sleepable(void);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __K_CRITICAL_H__ */
